# coding=utf-8
import uuid, math, copy
import os, sys
import re
import shutil, hashlib, datetime
from osgeo import gdal
from osgeo import ogr

from controller.controller_base import *
from conf import constant, settings
from common.write_log import writelog
from common.write_object import write_object
import dbs.models
from conf.json_map import TEMP_DATA_EXTEND

from common.gis_transform import GisTransform, GoemTransform


class EntryController(ControllerBase):

    def __init__(self, old_srid, new_srid=GIS_SRID_WEBMERCATOR, extend_info=TEMP_DATA_EXTEND):
        super(EntryController, self).__init__()
        # 为了支持中文路径，请添加下面这句代码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "NO")
        # 为了使属性表字段支持中文，请添加下面这句
        gdal.SetConfigOption("SHAPE_ENCODING", "")
        # 注册所有的驱动
        ogr.RegisterAll()
        # 数据格式的驱动
        self.driver = ogr.GetDriverByName('ESRI Shapefile')
        self.gis_tool = GisTransform(old_srid, new_srid)  # gis 点对点转换器
        self.geom_trans = GoemTransform(self.gis_tool)  # goem 操作对象

        self.insert_slow = False    # 是否慢速导入
        # self.insert_slow = True    # 是否慢速导入
        self.extend_info = copy.deepcopy(extend_info)     # 批次扩展信息 必须深拷贝不然内存没释放的话 紧接着的第二个导入计数就错了
        self.file_id = None     # 文件id
        self.upload_current_count = 0   # 当前导入了多少条数据
        self.upload_data_count = 0  # 本次导入的数据总条数
        self.sub_set_id = None  # 本次操作的归属数据子集id
        self.batch_id = None    # 本次操作的batch orm 对象
        self.layer_table_map = {  # 获取对应table
            'bridge': dbs.models.Bridge,
            'building': dbs.models.Building,
            'culvert': dbs.models.Culvert,
            'deceleration': dbs.models.Deceleration,
            'delineater': dbs.models.Delineater,
            'guardbar': dbs.models.GuardBar,
            'illumination': dbs.models.Illumination,
            'landuse': dbs.models.LandUse,
            'roadline': dbs.models.RoadLine,
            'roadpolygon': dbs.models.RoadPolygon,
            'roadsign': dbs.models.RoadSign,
            'treelawn': dbs.models.TreeLawn,
            'tunnel': dbs.models.Tunnel,
            'water': dbs.models.Water
        }
        self.geo_layer_map = {  # 获取geo层的对应字典表码
            'bridge': constant.GEO_LAYER_BRIDGE,
            'building': constant.GEO_LAYER_BUILDING,
            'culvert': constant.GEO_LAYER_CULVERT,
            'deceleration': constant.GEO_LAYER_DECELERATION,
            'delineater': constant.GEO_LAYER_DELINEATER,
            'guardbar': constant.GEO_LAYER_GUARDBAR,
            'illumination': constant.GEO_LAYER_ILLUMINATION,
            'landuse': constant.GEO_LAYER_LANDUSE,
            'roadline': constant.GEO_LAYER_ROADLINE,
            'roadpolygon': constant.GEO_LAYER_ROADPOLYGON,
            'roadsign': constant.GEO_LAYER_ROADSIGN,
            'treelawn': constant.GEO_LAYER_TREELAWN,
            'tunnel': constant.GEO_LAYER_TUNNEL,
            'water': constant.GEO_LAYER_WATER
        }
        self.entry_params_enum = [  # 允许导入postgis的字段  其中'geom'后面直接插入,不经过此处校验
            'global_id',
            'shape_length',
            'shape_area',
            'remark',
            'in_date',
            'va_date',
            'gis_class',
            'code',
            'name',
            'height'
        ]
        self.shp_postgis_map = {  # gdb字段名 与 postgis字段名 的映射
            'global_id': 'global_id',
            'geom': 'geom',
            'shape_length': 'shape_length',
            'shape_area':  'shape_area',
            'in_date': 'in_date',
            'va_date': 'va_date',
            'remark': 'remark',
            # 'code': 'code',
            # 'name': 'name',
            # 'bridge_code': 'code',
            # 'tunnel_code': 'code',
            # 'water_code': 'code',
            # 'road_name': 'name',
            # 'bridge_name': 'name',
            # 'building_name': 'name',
            # 'tunnel_name': 'name',
            # 'water_name': 'name',
            'class': 'gis_class',
            'Class': 'gis_class',
            'Name': 'name',
            'Height': 'height',
            'height': 'height'
        }
        self.geometry_type_map = {  # 获取对应 GEO 数据类型
            'bridge': constant.GEO_TYPE_MULTIPOLYGON,
            'building': constant.GEO_TYPE_MULTIPOLYGON,
            'culvert': constant.GEO_TYPE_MULTIPOLYGON,
            'deceleration': constant.GEO_TYPE_MULTIPOLYGON,
            'delineater': constant.GEO_TYPE_MULTILINESTRING,
            'guardbar': constant.GEO_TYPE_MULTILINESTRING,
            'illumination': constant.GEO_TYPE_POINT,
            'landuse': constant.GEO_TYPE_MULTIPOLYGON,
            'roadline': constant.GEO_TYPE_MULTILINESTRING,
            'roadpolygon': constant.GEO_TYPE_MULTIPOLYGON,
            'roadsign': constant.GEO_TYPE_POINT,
            'treelawn': constant.GEO_TYPE_MULTIPOLYGON,
            'tunnel': constant.GEO_TYPE_MULTIPOLYGON,
            'water': constant.GEO_TYPE_MULTIPOLYGON
        }

    def __change_goem(self, wkt_geom):   # 参考wkt_geom: 'POLYGON ((114.09538 22.62663,114.095389 22.626589))'
        '''构造新的goem'''
        new_goem, this_geo_type = self.geom_trans.get_finally_text(wkt_geom)  # POLYGON, ((114.090249091 22.6293011688,114.090258099 22.6292601749))
        return new_goem, this_geo_type

    def open_file(self, file_path, file_name):
        """打开File"""
        path = os.path.join(file_path, file_name)
        # dataset = gdal.OpenEx(path)   #  用于打开GDP文件
        dataset = self.driver.Open(path)
        if dataset is None:
            writelog("Open FileGeodataBase failed.%s\n" % (file_path+file_name))
            raise Exception
        return dataset

    # def backup_gdb(self, file_path, file_name):
    #     """备份文件"""
    #     try:
    #         gdb_backup_path = settings.settings.get('GDB_BACKUP_PATH')
    #         now = datetime.datetime.now().strftime('%Y%m%d_%H_%M_%S')
    #         # md5 = hashlib.md5()
    #         # md5.update((file_path+file_name+now).encode('utf-8'))
    #         backup_file_name = now + '_' + file_name
    #         shutil.move(file_path+file_name, gdb_backup_path+backup_file_name)
    #         db.session.query(ImportFile).filter(ImportFile.file_id == self.file_id).update(
    #                 {"backup_path": gdb_backup_path})
    #         db.session.commit()
    #     except:
    #         writelog('----------------------备份文件失败----------------------')
    #         raise

    def insert(self, params):
        """数据库插入一行"""
        temporary = TemporaryData(
            sub_set_id=self.sub_set_id or None,
            global_id=params.get('global_id'),
            geo_layer=params.get('geo_layer'),
            gis_class=params.get('gis_class'),
            code=params.get('code'),
            name=params.get('name'),
            in_date=params.get('in_date'),
            va_date=params.get('va_date'),
            remark=params.get('remark'),
            shape_length=params.get('shape_length'),
            shape_area=params.get('shape_area'),
            geo_type=params.get('geo_type'),
            geom=params.get('geom'),
            batch_id=self.batch_id,
            check_status=params.get('check_status'),
            origin_info=params.get('origin_info'),
            height=params.get('height')
        )
        db.session.add(temporary)
        db.session.commit()


    def insert_list(self, params_list):
        """数据库插入多行"""
        try:
            temporary_list = []
            for params in params_list:

                temporary = TemporaryData(
                    sub_set_id=self.sub_set_id or None,
                    gis_id=params.get('gis_id'),
                    global_id=params.get('global_id'),
                    geo_layer=params.get('geo_layer'),
                    gis_class=params.get('gis_class'),
                    code=params.get('code'),
                    name=params.get('name'),
                    in_date=params.get('in_date'),
                    va_date=params.get('va_date'),
                    remark=params.get('remark'),
                    shape_length=params.get('shape_length'),
                    shape_area=params.get('shape_area'),
                    geo_type=params.get('geo_type'),
                    geom=params.get('geom'),
                    batch_id=self.batch_id,
                    check_status=params.get('check_status'),
                    check_remark=params.get('check_remark'),
                    origin_info=params.get('origin_info'),
                    height=params.get('height')
                )
                temporary_list.append(temporary)

            db.session.add_all(temporary_list)
            db.session.commit()
        except:
            self.extend_info['batch_info']['abnormal_data'] += len(params_list)
            writelog(traceback.format_exc())
            write_object(params_list)

    def get_feature_param_dict(self, feat, defns, geo_layer_name):
        """
        从要素(行)对象获取参数
        :param feat: 行对象
        :param defns: 列集合对象
        :param layer_name: 图层名
        :return: params
        """
        params = {}
        params['origin_info'] = {}

        geo_layer = self.geo_layer_map.get(geo_layer_name)
        if not geo_layer:
            return None

        defn_count = defns.GetFieldCount()  # 定义数量(列数量)
        for i in range(defn_count):
            defn = defns.GetFieldDefn(i)    # 列对象
            defn_name = defn.GetName().lower()    # 列名
            column_name = self.shp_postgis_map.get(defn_name)   # 取对应postgis列名

            if not defn_name.lower() in ['name', 'py', 'engname', 'pathname']:
                params['origin_info'][defn_name] = feat.GetField(i)   # 记录原始数据
            if column_name not in self.entry_params_enum:
                continue
            params[column_name] = feat.GetField(i)  # 取出行中i列的数据

        geom = feat.GetGeometryRef()
        if geom:
            wkt_geom = geom.ExportToIsoWkt()
            params['origin_info']['geom'] = wkt_geom    # 记录原始goem
            wkt_geom = wkt_geom.strip()  # 去掉头和尾 蛋白质是牛肉6倍
            new_wkt_geom, this_geo_type = self.__change_goem(wkt_geom)
            params['geom'] = new_wkt_geom
            params['geo_type'] = this_geo_type

        params['gis_id'] = uuid.uuid1().get_hex()
        params['sub_set_id'] = self.sub_set_id
        params['geo_layer'] = geo_layer
        params['check_status'] = CHECK_STATUS_NORMAL
        return params

    def slow_upload_data(self, layer, geo_layer_name):
        """逐条慢速向数据库中导入数据"""
        '''提取数据,声明变量'''
        layer_code = self.geo_layer_map.get(geo_layer_name)

        layer_name = layer.GetName()    # 图层名(表名)
        feature_definitions = layer.GetLayerDefn()    # 要素定义集合对象(列集合)
        # geometry_type = feature_definition.GetGeomType()    # GEO 类型(暂时不用)
        # feature_count = layer.GetFeatureCount()  # 要素数量(行数量)

        '''遍历表的每一行'''
        for feature in layer:
            self.upload_current_count += 1      # 导入总进度游标

            insert_params = self.get_feature_param_dict(feature, feature_definitions, geo_layer_name)

            if insert_params is not None:
                try:
                    self.insert(insert_params)
                    self.extend_info['batch_info']['new_data'] += 1
                    self.extend_info['batch_info']['count'][layer_code] += 1
                except:
                    self.extend_info['batch_info']['abnormal_data'] += 1
                    writelog("此条数据插入失败:{geom}".format(geom=insert_params['origin_info']['geom']))
                    write_object(insert_params)
                    raise


            if self.upload_current_count % 1000 == 0:    # 每导存够1000条记录一次
                # 每导入1000条记录一下
                overall_upload_percent = self.upload_current_count/(self.upload_data_count*1.0) * 100   # 总进度百分比
                progress = round(overall_upload_percent, 2)     # 保留2位小数
                self.db.session.query(ImportFile).filter(ImportFile.file_id == self.file_id).update({"import_progress": progress})
                self.db.session.commit()
                writelog("全局已导入{0}条 全局总进度:{1}% ".format(self.upload_current_count, progress))



    def upload_data(self, layer, geo_layer_name):
        """向数据库中导入数据"""
        '''提取数据,声明变量'''
        layer_code = self.geo_layer_map.get(geo_layer_name)

        layer_name = layer.GetName()    # 图层名(表名)
        feature_definitions = layer.GetLayerDefn()    # 要素定义集合对象(列集合)
        # geometry_type = feature_definition.GetGeomType()    # GEO 类型(暂时不用)
        # feature_count = layer.GetFeatureCount()  # 要素数量(行数量)
        params_list = []  # 数据库可以批量插入,这里每存1000个插入一次

        '''遍历表的每一行'''
        for feature in layer:
            self.upload_current_count += 1      # 导入总进度游标
            insert_params = self.get_feature_param_dict(feature, feature_definitions, geo_layer_name)

            if insert_params is not None:
                if insert_params.get('geom') is not None:
                    params_list.append(insert_params)
                    self.extend_info['batch_info']['new_data'] += 1
                    self.extend_info['batch_info']['count'][layer_code] += 1
                else:
                    insert_params['check_status'] = CHECK_STATUS_ABNORMAL
                    params_list.append(insert_params)
                    self.extend_info['batch_info']['abnormal_data'] += 1
            else:
                self.extend_info['batch_info']['abnormal_data'] += 1

            if self.upload_current_count % 1000 == 0:    # 每导存够100条导入一次
                self.insert_list(params_list)
                params_list = []
                # 每导入1000条记录一下
                overall_upload_percent = self.upload_current_count/(self.upload_data_count*1.0) * 100   # 总进度百分比
                progress = round(overall_upload_percent, 2)     # 保留2位小数
                self.db.session.query(ImportFile).filter(ImportFile.file_id == self.file_id).update({"import_progress": progress})
                self.db.session.commit()
                writelog("全局已导入{0}条 全局总进度:{1}% ".format(self.upload_current_count, progress))
        # 剩下的零头导入
        if len(params_list) != 0:
            self.insert_list(params_list)

    def entry_one_shp(self, file_path, file_name):
        """导入一个shp"""
        dataset = self.open_file(file_path=file_path, file_name=file_name)  # 取出原始数据集
        layer_count = dataset.GetLayerCount()  # 图层(表)数
        for i in range(layer_count):
            layer = dataset.GetLayer(i)  # 图层(整张表的所有行)
            try:
                geo_layer_name = file_name.rstrip('.shp')  # 与苏总约定的文件名为图层名
                if self.insert_slow:
                    self.slow_upload_data(layer, geo_layer_name)  # 慢速导入
                else:
                    self.upload_data(layer, geo_layer_name)   # 快速导入

            except:
                writelog("导入图层失败")
                raise

    def record_upload_data_count(self, file_path, file_name):
        """记录总数"""
        dataset = self.open_file(file_path=file_path, file_name=file_name)  # 取出原始数据集
        layer_count = dataset.GetLayerCount()  # 图层(表)数
        # layers = []  # 准备导入的数据
        for i in range(layer_count):
            layer = dataset.GetLayer(i)  # 图层(整张表的所有行)
            # layer_name = layer.GetName()  # 表名
            # layers.append(layer)
            feature_count = layer.GetFeatureCount()  # 每层的行数
            self.upload_data_count += feature_count  # 总数
        dataset.Destroy()

    def entry(self, sub_set_id, batch_id, import_file):
        """执行导入任务"""
        self.file_id = import_file.file_id  # 记录文件id
        self.batch_id = batch_id        # 批次id
        self.sub_set_id = sub_set_id    # 本次操作的归属数据子集
        # self.insert_slow = True   #  慢速导入

        if import_file.file_name.endswith('.shp'):  # 导入文件夹如果是 .shp 直接导入
            self.record_upload_data_count(import_file.file_path, import_file.file_name)
            self.entry_one_shp(import_file.file_path, import_file.file_name)
        else:   # 如果是 N个.shp
            entry_list = []
            list_dir = os.listdir(import_file.file_path+import_file.file_name)  # linux 文件list
            for file_name in list_dir:
                if file_name.endswith('.shp'):
                    entry_list.append(file_name)    # 记录所有.shp文件
                    self.record_upload_data_count(import_file.file_path+import_file.file_name, file_name)   # 记录总数
            for entry_file_name in entry_list:  # 循环导入所有.shp
                self.entry_one_shp(import_file.file_path+import_file.file_name, entry_file_name)

        db.session.query(Batch).filter(Batch.batch_id==batch_id).update({'extend_info': self.extend_info})
        db.session.commit()
        db.session.query(ImportFile).filter(ImportFile.file_id == self.file_id).update(
            {"import_progress": 100})
        db.session.commit()
        writelog('-----------------导入完成end-------------------')

        # self.backup_gdb(file_path=import_file.file_path, file_name=import_file.file_name)  # 目前不备份

